#pragma once
#include "define.h"
#include "hdf5.h"
#include "H5Cpp.h"

#define HDF5_MAX_STRING 256

void write_strings(H5::Group& group, const H5std_string& name, const svector &strings);

void read_strings(H5::Group& group, const H5std_string& name, svector& strings);

void write_string(H5::Group& group, const H5std_string& name, const string_t &data);

void read_string(H5::Group& group, const H5std_string& name, string_t &data);

template<typename T>
void write_dataset(H5::Group& group, const H5std_string& name, int size, T* data)
{
	try {
		using namespace H5;

		DataType dataType;
		if (typeid(T) == typeid(int)) {
			dataType = DataType(PredType::NATIVE_INT);
		}
		else if (typeid(T) == typeid(long long int)) {
			dataType = DataType(PredType::NATIVE_LLONG);
		}
		else if (typeid(T) == typeid(double)) {
			dataType = DataType(PredType::NATIVE_DOUBLE);
		}
		else if (typeid(T) == typeid(long double)) {
			dataType = DataType(PredType::NATIVE_LDOUBLE);
		}
		else if (typeid(T) == typeid(char)) {
			dataType = DataType(PredType::NATIVE_CHAR);
		}
		else {
			printf("\n unsupport data type : %s, size = %d", typeid(T).name(), sizeof(T));
			throw;
		}

		//
		hsize_t dims[1] = { size };

		// Create the dataspace for a dataset first.
		DataSpace dataspace(1, dims);

		// Create the dataset under group with specified dataspace.      
		DataSet dataset = group.createDataSet(name, dataType, dataspace);

		// Write data in buffer to dataset.
		dataset.write(data, dataType);
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to write data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
};

template<typename T>
void read_dataset(H5::Group& group, const H5std_string& name, std::vector<T> &data)
{
	try {
		using namespace H5;
		
		DataSet  dataset  = group.openDataSet(name);		
		DataType dataType = dataset.getDataType();		

		// check datatype
		if ((dataType == DataType(PredType::NATIVE_INT) && typeid(T) == typeid(int)) ||
			(dataType == DataType(PredType::NATIVE_LLONG) && typeid(T) == typeid(long long int)) ||
			(dataType == DataType(PredType::NATIVE_DOUBLE) && typeid(T) == typeid(double)) ||
			(dataType == DataType(PredType::NATIVE_LDOUBLE) && typeid(T) == typeid(long double)) ||
			(dataType == DataType(PredType::NATIVE_CHAR) && typeid(T) == typeid(char))) {
			//
			DataSpace filespace = dataset.getSpace();
			data.resize(filespace.getSimpleExtentNpoints());
			dataset.read(data.data(), dataType, filespace, filespace);
		}
		else {
			std::cout
				<< "\nERROR : hdf5 data type : " << dataType.getTag()
				<< ", get type : " << typeid(T).name() << std::endl;
			throw;
		}		
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to read data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
};

template<typename T>
void write_scalar(H5::Group& group, const H5std_string& name, T &data)
{
	try {
		using namespace H5;

		DataSpace dataspace(H5S_SCALAR);

		DataType datatype; 
		if (typeid(T) == typeid(int)) {
			datatype = DataType(PredType::NATIVE_INT);
		}
		else if (typeid(T) == typeid(long long int)) {
			datatype = DataType(PredType::NATIVE_LLONG);
		}
		else if (typeid(T) == typeid(double)) {
			datatype = DataType(PredType::NATIVE_DOUBLE);
		}
		else if (typeid(T) == typeid(long double)) {
			datatype = DataType(PredType::NATIVE_LDOUBLE);
		}
		else {
			printf("\nERROR : type(%s) of data %s is unsupport", typeid(T).name(), name.c_str());
			throw;
		}	

		DataSet dataset = group.createDataSet(name, datatype, dataspace);
		dataset.write(&data, datatype);
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to write data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
};

template<typename T>
void read_scalar(H5::Group& group, const H5std_string& name, T &data)
{
	try {
		using namespace H5;

		DataSet   dataset = group.openDataSet(name);
		DataSpace filespace = dataset.getSpace();
		DataType  dataType  = dataset.getDataType();		
		
		if (filespace.getSimpleExtentNpoints() != 1) {// data size = 1
			printf("\nERROR : %s is not a scalar, size = %d",
				name.c_str(), filespace.getSimpleExtentNpoints());
			throw;
		}		

		// check datatype
		if ((dataType == DataType(PredType::NATIVE_INT) && typeid(T) != typeid(int)) ||
			(dataType == DataType(PredType::NATIVE_LLONG) && typeid(T) != typeid(long long int)) ||
			(dataType == DataType(PredType::NATIVE_DOUBLE) && typeid(T) != typeid(double)) ||
			(dataType == DataType(PredType::NATIVE_LDOUBLE) && typeid(T) != typeid(long double))) {
			std::cout
				<< "\nERROR : hdf5 data type : " << dataType.getTag()
				<< ", get type : " << typeid(T).name() << std::endl; 
			throw;
		}

		dataset.read(&data, dataType, filespace, filespace);
	}
	catch (...)
	{
		std::cout
			<< "\nERROR : failed to read data '" << name
			<< "' in group '" << group.getObjName() << "'" << std::endl;
		return;
	}
};

